home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / LinAlg 3.1 / LinAlg / README < prev    next >
Encoding:
Text File  |  1995-12-22  |  11.6 KB  |  278 lines  |  [TEXT/ALFA]

  1.             Numerical Math Class Library
  2.  
  3. ***** For version history, read on
  4.  
  5. ***** Highlights and idioms
  6.  
  7. 1. Never return complex objects (matrices or vectors)
  8. Danger: For example, when the following snippet 
  9.     Matrix foo(const int n)
  10.     { Matrix foom(n,n); fill_in(foom); return foom; }
  11.     Matrix m = foo(5);
  12. runs, it constructs matrix foo:foom, copies it onto stack as a return
  13. value and destroys foo:foom. Return value (a matrix) from foo() is
  14. then copied over to m (via a copy constructor), and the return value
  15. is destroyed. So, the matrix constructor is called 3 times and the
  16. destructor 2 times. For big matrices, the cost of multiple
  17. constructing/copying/destroying of objects may be very large. *Some*
  18. optimized compilers can cut down on 1 copying/destroying, but still it
  19. leaves at least two calls to the constructor. Note, LazyMatrices (see
  20. below) can construct Matrix m "inplace", with only a _single_ call to
  21. the constructor.
  22.  
  23.  
  24. 2. Use "two-address instructions"
  25.           "void Matrix::operator += (const Matrix& B);"
  26. as much as possible
  27. That is, to add two matrices, it's much more efficient to write
  28.     A += B;
  29. than
  30.     Matrix C = A + B;
  31. (if both operand should be preserved,
  32.     Matrix C = A; C += B;
  33. is still better).
  34.  
  35. 3. Use glorified constructors when returning of an object seems
  36. inevitable:
  37.         "Matrix A(Matrix::Transposed,B);"
  38.         "Matrix C(A,Matrix::TransposeMult,B);"
  39.  
  40. like in the following snippet (from vmatrix1.cc) that verifies that
  41. for an orthogonal matrix T, T'T = TT' = E.
  42.  
  43.     Matrix haar = haar_matrix(5);
  44.     Matrix unit(Matrix::Unit,haar);
  45.     Matrix haar_t(Matrix::Transposed,haar);
  46.     Matrix hth(haar,Matrix::TransposeMult,haar);
  47.     Matrix hht(haar,Matrix::Mult,haar_t);
  48.     Matrix hht1 = haar; hht1 *= haar_t;
  49.     verify_matrix_identity(unit,hth);
  50.     verify_matrix_identity(unit,hht);
  51.     verify_matrix_identity(unit,hht1);
  52.  
  53. 4. Accessing row/col/diagonal of a matrix without much fuss
  54. (and without moving a lot of stuff around)
  55.  
  56.       Matrix m(n,n); Vector v(n); MatrixDiag(m) += 4;
  57.       v = MatrixRow(m,1);
  58.       MatrixColumn m1(m,1); m1(2) = 3; // the same as m(2,1)=3;
  59. Note, constructing of, say, MatrixDiag does *not* involve any copying
  60. of any elements of the source matrix.
  61.  
  62. 5. It's possible (and encouraged) to use "nested" functions
  63. For example, creating of a Hilbert matrix can be done as follows:
  64.  
  65.    void foo(const Matrix& m)
  66.    {
  67.     Matrix m1(Matrix::Zero,m);
  68.     struct MakeHilbert : public ElementAction
  69.     {
  70.       void operation(REAL& element) { element = 1./(i+j-1); }
  71.     };
  72.     m1.apply(MakeHilbert());
  73.    }
  74.  
  75. of course, using a special method Matrix::hilbert_matrix() is still
  76. more optimal, but not by the whole lot. And that's right, class
  77. MakeHilbert is declared *within* a function and local to that
  78. function. It means one can define another MakeHilbert class (within
  79. another function or outside of any function, that is, in the global
  80. scope), and it still will be OK.
  81.  
  82. Another example is applying of a simple function to each matrix element
  83.  
  84.  void foo(Matrix& m, Matrix& m1)
  85.  {
  86.   class ApplyFunction : public ElementPrimAction
  87.   {
  88.     double (*func)(const double x);
  89.     void operation(REAL& element) { element = func(element); }
  90.     public: ApplyFunction(double (*_func)(const double x)) : func(_func) {}
  91.   };
  92.   m.apply(ApplyFunction(sin));
  93.   m1.apply(ApplyFunction(cos));
  94.  }
  95.  
  96. Validation code vmatrix.cc and vvector.cc contains a few more examples
  97. of that kind (especially vmatrix.cc:test_special_creation())
  98.  
  99. 6. Lazy matrices: instead of returning an object return a "recipe" how
  100. to make it. The full matrix would be rolled out only when and where
  101. it's needed:
  102.     Matrix haar = haar_matrix(5);
  103. haar_matrix() is a *class*, not a simple function. However similar
  104. this looks to a returning of an object (see note #1 above), it's
  105. dramatically different. haar_matrix() constructs a LazyMatrix, an
  106. object of just a few bytes long. A special "Matrix(const LazyMatrix&
  107. recipe)" constructor follows the recipe and makes the matrix haar()
  108. right in place. No matrix element is moved whatsoever!
  109.  
  110. Another example of matrix promises is
  111.   REAL array[] = {0,-4,0,  1,0,0,  0,0,9 };
  112.   test_svd_expansion(MakeMatrix(3,3,array,sizeof(array)/sizeof(array[0])));
  113.  
  114. Here, MakeMatrix is a LazyMatrix that promises to deliver a matrix
  115. filled in from a specified array. Function test_svd_expansion(const Matrix&)
  116. forces the promise: the compiler makes a temporary matrix, fills
  117. it in using LazyMatrix's recipe, passes it out to test_svd_expansion()
  118. function, Once the function returns, the temporary matrix is disposed of.
  119. All this goes behind the scenes. See vsvd.cc for more details (this is
  120. where the fragment was snipped from).
  121.  
  122. One more example is using Matrix/vector promises along with the
  123. Element actions:
  124.  
  125.     class square_add : public LazyMatrix, public ElementAction
  126.     {
  127.       const Vector &v1; Vector &v2;
  128.       void operation(REAL& element)
  129.               { assert(j==1); element = v1(i)*v1(i) + v2(i)*v2(i); }
  130.      void fill_in(Matrix& m) const { m.apply(*this); }
  131.      public: square_add(const Vector& _v1, Vector& _v2) :
  132.        LazyMatrix(_v1.q_row_lwb(),_v1.q_row_upb(),1,1),
  133.        v1(_v1), v2(_v2) {}
  134.     };
  135.     Vector vres = square_add(v2,v3);
  136.     Vector vres1 = v2; assert( !(vres1 == vres) );
  137.     verify_element_value(vres,1);
  138.     vres1 = square_add(v2,v3);
  139. Here square_add promises to deliver a vector with elements being sums
  140. of squares of elements of two other vectors. The promise is forced either
  141. by a Vector constructor, or by an assignment to another vector (in the
  142. latter case, a check is made that the dimensions of the promise are
  143. compatible with those of the target). In either case, computation of
  144. new vector elements is done "inplace", no temporary storage is ever
  145. allocated/used. Iteration is also done "behind the scenes", relieving the
  146. user of worries about index range checking, etc.
  147.  
  148. 7. SVD decomposition and its applications
  149. Class SVD performs a  Singular Value Decomposition of a rectangular matrix
  150. A = U * Sig * V'. Here, matrices U and V are orthogonal; matrix Sig is a
  151. diagonal matrix: its diagonal elements, which are all non-negative, are
  152. singular values (numbers) of the original matrix A. In another interpretation,
  153. the singular values are eigenvalues of matrix A'A.
  154.  
  155. Application of SVD: _regularized_ solution of a set of simultaneous
  156. linear equations Ax=B.  Matrix A does _not_ have to be a square matrix.
  157. If A is a MxN rectangular matrix with M>N, the set Ax=b is obviously
  158. overspecified. The solution x produced by SVD_inv_mult would be then 
  159. the least-norm solution, that is, a least-squares solution.
  160. Note, B can be either a vector (Mx1-matrix), or a "thick" matrix. If B is
  161. chosen to be a unit matrix, then x is A's inverse, or a pseudo-inverse if
  162. A is non-square and/or singular.
  163. An example of using SVD:
  164.     SVD svd(A);
  165.     cout << "condition number of matrix A " << svd.q_cond_number();
  166.     Vector x = SVD_inv_mult(svd,b);        // Solution of Ax=b
  167. Note that SVD_inv_mult is a LazyMatrix. That is, the actual computation
  168. occurs not when the object SVD_inv_mult is constructed, but when it's
  169. required (in an assignment).
  170.  
  171. ***** Grand plans
  172.     computing a random orthogonal matrix
  173.     use M_PI instead of PI (in the fft package)
  174.     saving/loading of a matrix
  175.     finding matrix Extrema (and extrema of abs(matrix))
  176.     compute X'AX for a square matrix
  177.     compute x'Ax (a square form)
  178.     asymmetry index
  179.     add ArithmeticProgression class ("virtual" constant vector)
  180.     In fft, overload * and / for the direct/inverse FFT: FFT f(n);
  181.         Vk = f*V; V = Vk/f;
  182.     make Matrix(ElementAction& action, const Matrix& prototype);
  183.     Make a special class for SymmetricMatrix
  184.     Make Matrix, symmetric matrix, etc. inherit from the class
  185.     matrixstorage, which holds refs to the data storage (starting
  186.     ptr, the end pointer, the number of elements) and can perform
  187.     element-by-element operations like assignment, assignment of a
  188.     scalar, etc.
  189.     When gcc starts supporting member function templates, make
  190.     iterator classes for iterating over a vector, two vectors, Matrix
  191.     slices, etc.
  192.     Use <const_cast> when gcc starts supporting it
  193.     Make procedures to perform row/col rotations, and use it
  194.     in SVD
  195.     Code to verify a matrix (pseudo)inverse, that is,
  196.     test Moore-Penrose  conditions XAX = X, AXA = A; AX and XA are
  197.     symmetric (that is, AX = (AX)', etc.) where X is a (pseudo)inverse
  198.     of A. Another SVD application: compute a covariance matrix for a
  199.     given design matrix X, i.e. find the inverse of the X'X for a
  200.     rectangular matrix X.
  201.      Add ispline(): building a spline and integrating it
  202.     Add slehol: solve a set of SLE with a symmetric matrix
  203.     
  204. ***** Revision history
  205.  
  206. Version 3.2
  207.     hjmin(), Hooks-Jeevse optimization, embellished and tested
  208.     ali.cc beautified, using nested functions, etc. (nice style)
  209.     Added SVD, singular value decomposition, and a some code
  210.     to use it (Solving Ax=b, where A doesn't have to be rectangular,
  211.     and b doesn't have to be a vector)
  212.     Minor embellishments
  213.     using bool datatype wherever appropriate
  214.     short replaced by 'int' as a datatype for indices, etc.: all
  215.     modern CPUs handle int more efficiently than short
  216.     (and memory isn't that of a problem any more)
  217.     Forcing promise (LazyMatrix) on assignment
  218.     Testing Matrix(Matrix::Inverted,m) glorified constructor
  219.     added retrieving an element from row/col/diag
  220.     added Matrix C.mult(A,B);    // Product A*B
  221.     *= operation on Matrix slices
  222.     Making a vector from a LazyMatrix
  223.     made sure that if memory is allocated via new, it's disposed
  224.     of via delete; if it was allocated via malloc/calloc, it's
  225.     disposed of via free(). It's important for CodeWarrior, where
  226.     new and malloc() are completely different beasts.
  227.     
  228.  
  229. Version 3.1
  230.     Deleted dummy destructors (they're better left inferred: it results
  231.     in more optimal code, especially in a class with virtual functions)
  232.     Minor tweaking and embellishments to please gcc 2.6.3
  233.     #included <float.h> and <minmax.h> where missing
  234.  
  235. Version 3.0 (beta): only Linear Algebra package was changed
  236.     got rid of a g++ extension when returning objects (in a_columns, etc)
  237.     removed "inline MatrixColumn Matrix::a_column(const int coln) const"
  238.       and likes: less problems with non-g++ compilers, better portability
  239.     Matrix(operation::Transpose) constructors and likes, 
  240.       (plus like-another constructor) Zero:, Unit, constructors
  241.     invert() matrix
  242.     true copy constructor (*this=another; at the very end)
  243.     fill in the matrix (with env) by doing ElementAction
  244.  
  245.     cleaned Makefile (pruned dead wood, don't growl creating libla
  246.       for the first time), a lots of comments as to how to make things
  247.     used M_PI instead of PI
  248.     #ifndef around GNU #pragma's
  249.     REAL is introduced via 'typedef' rather than via '#define': more
  250.       portable and flexible
  251.     added verify_element_value(), verify_matrix_identity() to the main
  252.       package (used to be local functions). They are useful in
  253.       writing the validation code
  254.     added inplace and regular matrix multiplication: computing A*B and A'*B
  255.     all matrix multiplication code is tested now
  256.     implemented A*x = y (inplace (w/resizing))
  257.     Matrix::allocate() made virtual
  258.     improved allocation of vectors (more optimal now)
  259.     added standard function to make an orthogonal Haar matrix
  260.       (useful for testing/validating purposes)
  261.     Resizing a vector keeps old info now (expansion adds zeros (but still
  262.       keeps old elements)
  263.     universal matrix creator from a special class: Lazy Matrices
  264.  
  265. Version 2.0, Mar 1994: Only LinAlg package was changed significantly
  266.     Linear Algebra library was made more portable and compiles now
  267.       under gcc 2.5.8 without a single warning.
  268.     Added comparisons between a matrix and a scalar (for-every/all -type
  269.       comparisons)
  270.     More matrix slice operators implemented
  271.       (operations on a col/row/diag of a matrix, assignments them
  272.       to/from a vector)
  273.     Other modules weren't changed (at least, significantly), but work
  274.       fine with the updated libla library
  275.  
  276. Version 1.1, Mar 1992 (initial revision)
  277.  
  278.